HTML5 高级 API 面试题全解析
一、核心要点速览
💡 核心考点
- Web Workers: 多线程编程,后台处理耗时任务
- Geolocation: 地理定位 API 及使用
- Drag & Drop: 拖放功能实现
- History API: 单页应用路由基础
- 其他 API: Page Visibility、Clipboard 等
二、Web Workers 多线程
1. 基础用法
javascript
// 主线程 (main.js)
const worker = new Worker('worker.js')
// 发送数据给 worker
worker.postMessage({ number: 1000000 })
// 接收 worker 的消息
worker.onmessage = (e) => {
console.log('计算结果:', e.data)
worker.terminate() // 终止 worker
}
// 错误处理
worker.onerror = (error) => {
console.error('Worker 错误:', error)
}
// 关闭 worker
// worker.terminate()javascript
// Worker 线程 (worker.js)
onmessage = (e) => {
const number = e.data.number
// 执行耗时计算
const result = heavyComputation(number)
// 返回结果
postMessage(result)
// 也可以关闭自己
// close()
}
function heavyComputation(n) {
let sum = 0
for (let i = 0; i < n; i++) {
sum += i
}
return sum
}2. 实际应用场景
javascript
// 场景 1: 大数据处理
const worker = new Worker('process.js')
// 发送大量数据
fetch('/api/large-data')
.then(res => res.json())
.then(data => {
worker.postMessage(data)
})
worker.onmessage = (e) => {
console.log('处理完成:', e.data)
displayResult(e.data)
}
// process.js
onmessage = (e) => {
const data = e.data
// 复杂的数据处理
const result = data.map(item => {
// 耗时操作...
return transform(item)
})
postMessage(result)
}javascript
// 场景 2: 实时计算
// 主线程
const calcWorker = new Worker('calculator.js')
input.addEventListener('input', (e) => {
const value = e.target.value
calcWorker.postMessage(value)
})
calcWorker.onmessage = (e) => {
resultDisplay.textContent = e.data
}
// calculator.js
onmessage = (e) => {
const value = e.data
// 复杂计算
const result = complexCalculation(value)
postMessage(result)
}3. 使用限制
┌──────────────────────────────────────────────────────────┐
│ Web Workers 使用限制 │
└──────────────────────────────────────────────────────────┘
不能做的事:
✗ 访问 DOM 元素
└─ Worker 没有 window、document 对象
✗ 访问父页面的变量
└─ 只能通过 postMessage 通信
✗ 某些跨域脚本
└─ 必须同源或设置 CORS
适合的场景:
✓ 大数据处理(图像处理、文件解析)
✓ 复杂计算(加密解密、数据压缩)
✓ 后台任务(数据同步、心跳检测)
✓ 实时分析(日志分析、指标计算)
不适合的场景:
✗ 简单的异步任务(用 Promise)
✗ 需要 DOM 操作的任务
✗ 频繁通信的任务(通信有开销)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━4. SharedWorker(共享 worker)
javascript
// 主线程
const sharedWorker = new SharedWorker('shared-worker.js')
sharedWorker.port.start()
sharedWorker.port.postMessage('Hello from tab 1')
sharedWorker.port.onmessage = (e) => {
console.log('收到消息:', e.data)
}
// shared-worker.js
const ports = new Set()
onconnect = (e) => {
const port = e.ports[0]
ports.add(port)
port.start()
port.onmessage = (e) => {
// 广播给所有端口
ports.forEach(p => {
if (p !== port) {
p.postMessage(e.data)
}
})
}
}三、Geolocation 地理定位
1. 基础用法
javascript
// 获取当前位置
navigator.geolocation.getCurrentPosition(
// 成功回调
(position) => {
const { latitude, longitude, accuracy } = position.coords
const { altitude, heading, speed } = position.coords
console.log(`
纬度:${latitude}
经度:${longitude}
精度:${accuracy}米
海拔:${altitude}米
方向:${heading}度
速度:${speed}m/s
`)
// 在地图上标记位置
showOnMap(latitude, longitude)
},
// 错误回调
(error) => {
switch(error.code) {
case error.PERMISSION_DENIED:
console.error('用户拒绝授权')
break
case error.POSITION_UNAVAILABLE:
console.error('位置信息不可用')
break
case error.TIMEOUT:
console.error('获取超时')
break
default:
console.error('未知错误')
}
},
// 选项
{
enableHighAccuracy: true, // 高精度模式
timeout: 5000, // 超时 5 秒
maximumAge: 0 // 不使用缓存
}
)2. 持续追踪位置
javascript
// 持续追踪(如运动轨迹记录)
const watchId = navigator.geolocation.watchPosition(
(position) => {
const { latitude, longitude } = position.coords
// 更新地图上的位置
updatePosition(latitude, longitude)
// 记录轨迹
trackPoints.push({
lat: latitude,
lng: longitude,
time: Date.now()
})
},
(error) => {
console.error('定位失败:', error)
},
{
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 1000 // 1 秒内的缓存可用
}
)
// 停止追踪
function stopTracking() {
navigator.geolocation.clearWatch(watchId)
}3. 在地图上使用
javascript
// 结合百度地图/高德地图
navigator.geolocation.getCurrentPosition((position) => {
const { latitude, longitude } = position.coords
// 百度地图
const map = new BMap.Map('container')
const point = new BMap.Point(longitude, latitude)
map.centerAndZoom(point, 15)
// 添加标记
const marker = new BMap.Marker(point)
map.addOverlay(marker)
// 高德地图
const amap = new AMap.Map('container', {
center: [longitude, latitude],
zoom: 15
})
const amarker = new AMap.Marker({
position: [longitude, latitude]
})
amap.add(amarker)
})四、Drag & Drop 拖放 API
1. 基础拖放
html
<div draggable="true" id="dragItem">可拖动物体</div>
<div id="dropZone">放置区域</div>
<style>
#dragItem {
width: 100px;
height: 100px;
background: blue;
color: white;
cursor: move;
}
#dropZone {
width: 200px;
height: 200px;
border: 2px dashed #ccc;
margin-top: 20px;
}
#dropZone.dragover {
background: #f0f0f0;
}
</style>javascript
const dragItem = document.getElementById('dragItem')
const dropZone = document.getElementById('dropZone')
// 拖拽元素事件
dragItem.addEventListener('dragstart', (e) => {
// 设置拖拽数据
e.dataTransfer.setData('text/plain', e.target.id)
e.dataTransfer.effectAllowed = 'move'
// 自定义拖拽图像
// e.dataTransfer.setDragImage(image, x, y)
})
dragItem.addEventListener('dragend', (e) => {
console.log('拖拽结束')
e.target.style.opacity = '1'
})
// 放置区域事件
dropZone.addEventListener('dragenter', (e) => {
e.preventDefault()
dropZone.classList.add('dragover')
})
dropZone.addEventListener('dragover', (e) => {
e.preventDefault() // 必须调用才能放置
e.dataTransfer.dropEffect = 'move'
})
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('dragover')
})
dropZone.addEventListener('drop', (e) => {
e.preventDefault()
dropZone.classList.remove('dragover')
// 获取拖拽数据
const id = e.dataTransfer.getData('text/plain')
const draggable = document.getElementById(id)
// 将元素添加到放置区域
dropZone.appendChild(draggable)
})2. 拖放事件流程
时间 → ─────────────────────────────────────────────────►
完整拖放流程:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
用户按下元素
│
▼
┌─────────────┐
│ dragstart │ ← 开始拖拽
│ 设置数据 │
└──────┬──────┘
│
▼
┌─────────────┐
│ drag │ ← 持续触发(拖动中)
└──────┬──────┘
│
▼
┌─────────────┐
│ dragenter │ ← 进入目标区域
└──────┬──────┘
│
▼
┌─────────────┐
│ dragover │ ← 在目标区域移动
│ e.preventDefault()│
└──────┬──────┘
│
▼
┌─────────────┐
│ dragleave │ ← 离开目标区域
└──────┬──────┘
│
▼
┌─────────────┐
│ drop │ ← 释放鼠标
│ 获取数据 │
└──────┬──────┘
│
▼
┌─────────────┐
│ dragend │ ← 拖拽结束
└─────────────┘
事件触发顺序:
dragstart → drag → dragenter → dragover →
(dragleave) → drop → dragend
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━3. 文件拖放上传
javascript
const dropZone = document.getElementById('dropZone')
dropZone.addEventListener('drop', (e) => {
e.preventDefault()
const files = e.dataTransfer.files
// 处理文件
Array.from(files).forEach(file => {
console.log('文件:', file.name, file.size, file.type)
// 上传图片
if (file.type.startsWith('image/')) {
uploadFile(file)
}
})
})
function uploadFile(file) {
const formData = new FormData()
formData.append('file', file)
fetch('/upload', {
method: 'POST',
body: formData
})
.then(res => res.json())
.then(data => {
console.log('上传成功:', data)
})
}五、History API 历史管理
1. 基础用法
javascript
// 添加历史记录(不刷新页面)
history.pushState({ page: 1, title: '第一页' }, '第一页', '?page=1')
// 替换当前历史记录
history.replaceState({ page: 2 }, '第二页', '?page=2')
// 监听后退/前进按钮
window.addEventListener('popstate', (e) => {
console.log('状态:', e.state)
if (e.state) {
// 根据 state 渲染对应页面
renderPage(e.state.page)
}
})
// 导航
history.back() // 后退一页
history.forward() // 前进一页
history.go(-2) // 后退 2 页
history.go(2) // 前进 2 页2. 单页应用路由示例
javascript
class SimpleRouter {
constructor() {
this.routes = {}
window.addEventListener('popstate', (e) => {
this.loadRoute(location.pathname)
})
}
// 注册路由
addRoute(path, handler) {
this.routes[path] = handler
}
// 导航到指定路径
navigate(path, state = {}) {
history.pushState(state, '', path)
this.loadRoute(path)
}
// 加载路由
loadRoute(path) {
const handler = this.routes[path]
if (handler) {
handler()
} else {
console.error('404 Not Found')
}
}
}
// 使用示例
const router = new SimpleRouter()
router.addRoute('/', () => {
document.title = '首页'
document.querySelector('#app').innerHTML = '<h1>首页</h1>'
})
router.addRoute('/about', () => {
document.title = '关于'
document.querySelector('#app').innerHTML = '<h1>关于我们</h1>'
})
router.addRoute('/contact', () => {
document.title = '联系'
document.querySelector('#app').innerHTML = '<h1>联系方式</h1>'
})
// 初始加载
router.loadRoute(location.pathname)
// 导航
// router.navigate('/about')六、其他重要 API
1. Page Visibility API 页面可见性
javascript
// 监听页面可见性变化
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
// 页面隐藏(切换到其他标签或最小化)
console.log('页面隐藏')
// 暂停视频播放
video.pause()
// 停止动画
cancelAnimationFrame(animationId)
// 减少轮询频率
clearInterval(pollTimer)
} else {
// 页面可见
console.log('页面可见')
// 恢复播放
video.play()
// 恢复动画
animationId = requestAnimationFrame(animate)
// 恢复轮询
pollTimer = setInterval(poll, 5000)
}
})
// 检查当前状态
console.log(document.visibilityState)
// 'visible' | 'hidden' | 'prerender'2. Clipboard API 剪贴板
javascript
// 复制文本
async function copyText(text) {
try {
await navigator.clipboard.writeText(text)
console.log('复制成功')
showToast('复制成功')
} catch (err) {
console.error('复制失败:', err)
// 降级方案
fallbackCopy(text)
}
}
// 粘贴文本
async function pasteText() {
try {
const text = await navigator.clipboard.readText()
console.log('粘贴内容:', text)
return text
} catch (err) {
console.error('粘贴失败:', err)
}
}
// 复制富文本
async function copyHtml(html, text) {
const clipboardItem = new ClipboardItem({
'text/html': new Blob([html], { type: 'text/html' }),
'text/plain': new Blob([text], { type: 'text/plain' })
})
await navigator.clipboard.write([clipboardItem])
}
// 降级方案(老浏览器)
function fallbackCopy(text) {
const textarea = document.createElement('textarea')
textarea.value = text
textarea.style.position = 'fixed'
textarea.style.opacity = '0'
document.body.appendChild(textarea)
textarea.select()
try {
document.execCommand('copy')
console.log('复制成功(降级方案)')
} catch (err) {
console.error('复制失败')
}
document.body.removeChild(textarea)
}3. Fullscreen API 全屏
javascript
const element = document.getElementById('video')
// 进入全屏
async function enterFullscreen() {
try {
await element.requestFullscreen()
} catch (err) {
console.error('全屏失败:', err)
}
}
// 退出全屏
function exitFullscreen() {
if (document.fullscreenElement) {
document.exitFullscreen()
}
}
// 监听全屏变化
document.addEventListener('fullscreenchange', () => {
if (document.fullscreenElement) {
console.log('已进入全屏')
} else {
console.log('已退出全屏')
}
})
// 检查是否支持
if (document.documentElement.requestFullscreen) {
// 支持全屏
showFullscreenButton()
}七、面试标准回答
HTML5 提供了众多强大的 API 来增强 Web 应用能力。
Web Workers 允许 JavaScript 在后台线程运行,避免耗时操作阻塞主线程。适用于大数据处理、复杂计算等场景。但 Worker 不能访问 DOM,只能通过 postMessage 与主线程通信。
Geolocation API 提供地理定位功能,可以获取用户的经纬度、海拔、速度等信息。常用于地图应用、位置服务、运动追踪等场景。需要注意用户授权和隐私保护。
Drag & Drop API 实现元素的拖放功能。通过 dragstart、dragover、drop 等事件,可以实现文件上传、元素排序、拖放交互等功能。注意 dragover 事件必须调用 preventDefault 才能放置。
History API 包括 pushState、replaceState 和 popstate 事件,是单页应用路由的基础。可以在不刷新页面的情况下修改 URL 和管理历史记录。
其他实用 API 还包括:Page Visibility 监听页面可见性,用于优化性能;Clipboard API 操作剪贴板;Fullscreen API 控制全屏等。
实际项目中,我经常使用这些 API 来提升用户体验,比如用 Web Workers 处理大数据,用 Geolocation 实现位置服务,用 History API 构建 SPA 路由等。
八、记忆口诀
HTML5 API 歌诀:
Workers 多线程,
后台计算不卡顿。
Geolocation 来定位,
地图服务离不开。
Drag Drop 能拖放,
文件上传很方便。
History 管路由,
单页应用它做主。
Visibility 看可见,
性能优化靠它行。
Clipboard 复制粘,
Fullscreen 全屏现!九、推荐资源
十、总结一句话
- Web Workers: 多线程 + 后台计算 = 不阻塞主线程 ⚙️
- Geolocation: 地理定位 + 地图服务 = LBS 应用基础 📍
- Drag & Drop: 拖拽交互 + 文件上传 = 更好的用户体验 🖱️
- History API: 路由管理 + 单页应用 = SPA 核心技术 🔄
- Page Visibility: 可见检测 + 性能优化 = 智能资源管理 👁️